package streamEngine

import (
	"errors"
	"fmt"
	"gitee.com/fierce_wolf/go-fox-edge-channel-common/channelStatus"
	"gitee.com/fierce_wolf/go-fox-edge-common/commComp/serialPort"
	"gitee.com/fierce_wolf/go-fox-edge-common/commConfig"
	"gitee.com/fierce_wolf/go-fox-edge-common/commLogger"
	"gitee.com/fierce_wolf/go-fox-edge-common/commUtil/Charts"
	"gitee.com/fierce_wolf/go-fox-edge-common/commUtil/Hex"
	"gitee.com/fierce_wolf/go-fox-edge-common/commUtil/Map"
	"gitee.com/fierce_wolf/go-fox-edge-common/commUtil/Method"
	"gitee.com/fierce_wolf/go-fox-edge-common/commUtil/Number"
	"github.com/jacobsa/go-serial/serial"
	"go-fox-edge-channel-serial-analyzer/channelEntity"
	"go-fox-edge-channel-serial-analyzer/channelService"
	"go-fox-edge-channel-serial-analyzer/script"
	"time"
)

type streamEngine struct {
	openLogger   bool
	serialPort   *serialPort.SerialPort
	streamEntity *channelEntity.SerialStreamEntity
}

func (e *streamEngine) initialize() error {
	commLogger.Info("------------- 数据流分析引擎 初始化开始！-------------")

	serverConfig, err := commConfig.GetConfigParam("serverConfig")
	if err != nil {
		return err
	}
	serialParam := Map.GetOrDefault(serverConfig, "serialPort", make(map[string]interface{}))
	e.openLogger = Map.GetOrDefault(serverConfig, "logger", false).(bool)

	// 打开串口
	e.openSerial(serialParam.(map[string]interface{}))

	// 启动后台线程
	go e.scheduler()

	commLogger.Info("------------- 数据流分析引擎 初始化结束！-------------")

	return nil
}

func (e *streamEngine) openSerial(channelParam map[string]interface{}) (err error) {
	defer func() {
		if r := recover(); r != nil {
			err = errors.New(fmt.Sprintf("捕获到异常:%v", r))
		}
	}()

	serialName := Map.GetOrDefault(channelParam, "serialName", "").(string)
	baudRate := Number.MakeInt(Map.GetOrDefault(channelParam, "baudRate", 9600))
	dataBits := Number.MakeInt(Map.GetOrDefault(channelParam, "databits", 8))
	stopBits := Number.MakeInt(Map.GetOrDefault(channelParam, "stopbits", 1))
	parity := Map.GetOrDefault(channelParam, "parity", "N").(string)

	parityMode := serial.PARITY_NONE
	if parity == "N" {
		parityMode = serial.PARITY_NONE
	}
	if parity == "O" {
		parityMode = serial.PARITY_ODD
	}
	if parity == "E" {
		parityMode = serial.PARITY_EVEN
	}

	// 配置串口
	options := serial.OpenOptions{
		PortName:        serialName,     // 串口名称
		BaudRate:        uint(baudRate), // 波特率
		DataBits:        uint(dataBits), // 数据位
		StopBits:        uint(stopBits), // 停止位
		ParityMode:      parityMode,     // 校验位
		MinimumReadSize: 4,              // 最小读取字节数
	}

	// 打开串口
	err = e.serialPort.Open(options)
	if err != nil {
		commLogger.Error("打开串口失败:" + serialName)
		return err
	}

	commLogger.Info("打开串口成功:" + serialName)
	return nil
}

func (e *streamEngine) scheduler() {
	buff := make([]byte, 1024)

	for true {
		// 等待接收到数据的信号
		e.serialPort.Signal.Wait()

		e.listenPort(buff)
	}
}

func (e *streamEngine) listenPort(buff []byte) {
	// 定义一个函数级的异常拦截，用来防止可能的代码BUG
	defer func() {
		if r := recover(); r != nil {
			commLogger.Error(fmt.Sprintf("analyseData() 捕获到异常:%v", r))
		}
	}()

	// 检测：串口是否打开
	if !e.serialPort.IsOpen() {
		return
	}

	// 尝试读取数据到本线程中的StreamEntity
	cnt, err := e.serialPort.Read(buff, 1*1000)
	if err != nil {
		return
	}
	if cnt > 0 {
		e.streamEntity.AddTail(buff[:cnt])
	}

	// 检测：是否有数据
	if e.streamEntity.IsEmpty() {
		return
	}

	// 分析数据流
	channelName, datas := e.analyzeStream()
	if len(datas) == 0 {
		return
	}

	// 最近接收数据的更新时间
	channelStatus.UpdateParamStatus(channelName, "receiveTime", time.Now().UnixMilli())

	for _, data := range datas {
		e.reportTo(channelName, data)
	}

}

func (e *streamEngine) analyzeStream() (string, [][]byte) {
	channelName := ""
	datas := make([][]byte, 0)

	channelEntity.Map.Range(func(key, value interface{}) bool {
		serialChannelEntity := value.(*channelEntity.SerialChannelEntity)

		// 取出脚本引擎
		splitScript := serialChannelEntity.SplitScript
		if splitScript == nil {
			return true
		}

		// 尝试解码
		datas = e.decodes(serialChannelEntity, e.streamEntity)
		if len(datas) == 0 {
			return true
		}

		channelName = key.(string)
		return true
	})

	return channelName, datas
}

func (e *streamEngine) decodes(serialChannelEntity *channelEntity.SerialChannelEntity, streamEntity *channelEntity.SerialStreamEntity) [][]byte {
	// 定义一个函数级的异常拦截，用来防止可能的代码BUG
	defer func() {
		if r := recover(); r != nil {
			//res = false
			//err = errors.New(fmt.Sprintf("捕获到异常:%v", r))
		}
	}()

	datas := make([][]byte, 0)

	// 分拆解码器
	splitScript := serialChannelEntity.SplitScript
	if splitScript == nil {
		return datas
	}

	// 必选条件：分拆报文
	for true {
		// 尝试解码
		data, err := e.decode(splitScript, streamEntity)
		if err != nil || data == nil {
			break
		}

		// 对报文尝试进行身份识别
		if !e.checkKey(serialChannelEntity, data) {
			continue
		}

		// 保存数据
		datas = append(datas, data)
	}

	return datas
}

func (e *streamEngine) decode(splitMessage *script.Decoder, streamEntity *channelEntity.SerialStreamEntity) (res []byte, err error) {
	defer func() {
		if r := recover(); r != nil {
			res = nil
			err = errors.New(fmt.Sprintf("捕获到异常:%v", r))
		}
	}()

	val, err := splitMessage.Decode(streamEntity.GetBuff(), streamEntity.GetEnd())
	if err != nil {
		return nil, err
	}
	if val == "" {
		return nil, errors.New("未能分拆出有效报文")
	}

	if e.openLogger {
		commLogger.Console.Info("分拆出了报文:" + val)
	}

	// 转换数据格式
	data := Hex.DecodeString(val)

	// 移走数据
	streamEntity.MovHead(data)

	return data, nil
}

func (e *streamEngine) checkKey(serialChannelEntity *channelEntity.SerialChannelEntity, data []byte) bool {
	// 可选条件：如果用户没有配置，那么不进行检查，直接返回成功
	keyScript := serialChannelEntity.KeyScript
	if keyScript == nil {
		return true
	}

	// 提取业务特征
	key, err := keyScript.Decode(data, len(data))
	if err != nil {
		return false
	}
	if key == "" {
		return false
	}

	// 比较业务特征
	return key == serialChannelEntity.ChannelParam["serviceKey"]
}

func (e *streamEngine) reportTo(channelName string, data []byte) {
	entity, _ := channelEntity.Map.Load(channelName)
	if entity == nil {
		return
	}

	serialChannelEntity := entity.(*channelEntity.SerialChannelEntity)

	// 返回格式：是否进行文本转码
	message := ""
	returnText := serialChannelEntity.ChannelParam["returnText"]
	if !Method.HasEmpty(returnText) {
		// GB2312编码的字符串，在GO的字符库中标识为GBK
		if "GB2312" == returnText {
			gb2312, err := Charts.GbkToUtf8Str(data)
			if err != nil {
				return
			}

			message = gb2312
		}
	} else {
		// 住哪和成HEX格式的编码
		message = Hex.EncodeToString(data)
	}

	// 上报数据
	channelService.Report(channelName, message)

	// 记录日志
	if e.openLogger {
		commLogger.Console.Info("串口接收到数据:" + channelName + "\n" + message)
	}
}
